package com.gitee.wsl.android.ext.base

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import com.gitee.wsl.android.ext.ApplicationInit.application
import com.gitee.wsl.jvm.ext.base.DateTimeExt
import com.gitee.wsl.jvm.ext.base.currentDateTime
import com.gitee.wsl.jvm.ext.base.dateTimeFormatterOf
import com.gitee.wsl.jvm.ext.base.startOfToday
import java.time.Instant
import java.time.LocalDate
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import java.time.temporal.ChronoUnit
import java.time.temporal.TemporalAdjusters
import java.util.Date
import java.util.Locale
import java.util.TimeZone
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty

val systemZoneId: ZoneId by object : ReadOnlyProperty<Any?, ZoneId> {
    private lateinit var zoneId: ZoneId

    override fun getValue(thisRef: Any?, property: KProperty<*>): ZoneId {
        if (!::zoneId.isInitialized) {
            zoneId = ZoneId.systemDefault()
            application.registerReceiver(object : BroadcastReceiver() {
                override fun onReceive(context: Context, intent: Intent?) {
                    if (intent?.action == Intent.ACTION_TIMEZONE_CHANGED) {
                        TimeZone.setDefault(null)
                        zoneId = ZoneId.systemDefault()
                    }
                }
            }, IntentFilter(Intent.ACTION_TIMEZONE_CHANGED))
        }
        return zoneId
    }
}

inline val currentZoneDateTime: ZonedDateTime
    get() = currentDateTime.atZone(systemZoneId)


fun Instant.format(pattern: String, zone: ZoneId = systemZoneId, locale: Locale? = null): String =
    dateTimeFormatterOf(pattern, locale).withZone(zone).format(this)

fun LocalDateTime.toInstant(zone: ZoneId = systemZoneId): Instant =
    atZone(zone).toInstant()

fun Instant.toLocalDateTime(zone: ZoneId = systemZoneId): LocalDateTime =
    LocalDateTime.ofInstant(this, zone)

fun LocalDateTime.toEpochSecond(zone: ZoneId = systemZoneId): Long =
    atZone(zone).toEpochSecond()

fun LocalDateTime.toEpochMilli(zone: ZoneId = systemZoneId): Long =
    toEpochSecond(zone) * 1000 + toLocalTime().nano / 1000000

fun String.toInstant(pattern: String, zone: ZoneId = systemZoneId): Instant =
    ZonedDateTime.parse(this, DateTimeFormatter.ofPattern(pattern).withZone(zone)).toInstant()

fun String.toEpochMilliseconds(pattern: String, zone: ZoneId = systemZoneId): Long =
    toInstant(pattern, zone).toEpochMilli()

fun String.toEpochSeconds(pattern: String, zone: ZoneId = systemZoneId): Long =
    toInstant(pattern, zone).epochSecond

fun LocalDateTime.isToday(zone: ZoneId = systemZoneId): Boolean = toLocalDate().isToday(zone)

fun LocalDate.isToday(zone: ZoneId = systemZoneId): Boolean = this == LocalDate.now(zone)

fun LocalDateTime.isYesterday(zone: ZoneId = systemZoneId): Boolean = toLocalDate().isYesterday(zone)

fun LocalDate.isYesterday(zone: ZoneId = systemZoneId): Boolean =
    this == LocalDate.now(zone).minus(1, ChronoUnit.DAYS)

fun Context.is24HourFormat(): Boolean = android.text.format.DateFormat.is24HourFormat(this)

/**
 * Return the friendly time span by now.
 *
 * @param millis The milliseconds.
 * @return the friendly time span by now
 *
 *  * 如果小于 1 秒钟内，显示刚刚
 *  * 如果在 1 分钟内，显示 XXX秒前
 *  * 如果在 1 小时内，显示 XXX分钟前
 *  * 如果在 1 小时外的今天内，显示今天15:32
 *  * 如果是昨天的，显示昨天15:32
 *  * 其余显示，2016-10-15
 *  * 时间不合法的情况全部日期和时间信息，如星期六 十月 27 14:21:20 CST 2007
 *
 */
fun DateTimeExt.getFriendlyTimeSpanByNow(millis: Long): String {
    val now = System.currentTimeMillis()
    val span = now - millis
    if (span < 0) // U can read http://www.apihome.cn/api/java/Formatter.html to understand it.
        return String.format("%tc", millis)
    if (span < 1000) {
        return "刚刚"
    } else if (span < DateTimeExt.TimeConstants.MIN) {
        return java.lang.String.format(Locale.getDefault(), "%d秒前", span / DateTimeExt.TimeConstants.SEC)
    } else if (span < DateTimeExt.TimeConstants.HOUR) {
        return java.lang.String.format(Locale.getDefault(), "%d分钟前", span / DateTimeExt.TimeConstants.MIN)
    }
    // 获取当天 00:00
    val wee: Long = startOfToday.toEpochMilli()
    return if (millis >= wee) {
        String.format("今天%tR", millis)
    } else if (millis >= wee - DateTimeExt.TimeConstants.DAY) {
        String.format("昨天%tR", millis)
    } else {
        String.format("%tF", millis)
    }
}


fun LocalDateTime.asDate() : Date {
    return Date.from(atZone(systemZoneId).toInstant())
}

fun LocalDate.asDate() : Date {
    return Date.from(atStartOfDay().atZone(systemZoneId).toInstant())
}

fun DateTimeExt.asMillisLocalDateTime(milliseconds: Long) : LocalDateTime {
    return Instant.ofEpochMilli(milliseconds)
        .atZone(ZoneId.systemDefault())
        .toLocalDateTime()
}

fun DateTimeExt.asMillisLocalDate(milliseconds: Long) : LocalDate {
    return Instant.ofEpochMilli(milliseconds)
        .atZone(systemZoneId)
        .toLocalDate()
}
/**
 * 获取某季度的第一天
 * 第一季度：1月－3月
 * 第二季度：4月－6月
 * 第三季度：7月－9月
 * 第四季度：10月－12月
 * @param date 当前日期LocalDate
 * @return LocalDate
 */
fun LocalDate.firstDayOfQuarter(): LocalDate {
    val month = month.value
    return when {
        month in 4..6 -> withMonth(4).with(TemporalAdjusters.firstDayOfMonth())
        month in 7..9 -> withMonth(7).with(TemporalAdjusters.firstDayOfMonth())
        month >= 10 -> withMonth(10).with(TemporalAdjusters.firstDayOfMonth())
        else -> withMonth(1).with(TemporalAdjusters.firstDayOfMonth())
    }
}

/**
 * 获取某季度的最后一天
 * 第一季度：1月－3月
 * 第二季度：4月－6月
 * 第三季度：7月－9月
 * 第四季度：10月－12月
 *
 * @param date 当前日期LocalDate
 * @return LocalDate
 */

fun LocalDate.lastDayOfQuarter(): LocalDate {
    val month = month.value
    return when {
        month in 4..6 -> withMonth(6).with(TemporalAdjusters.lastDayOfMonth())
        month in 7..9 -> withMonth(9).with(TemporalAdjusters.lastDayOfMonth())
        month >= 10 -> withMonth(12).with(TemporalAdjusters.lastDayOfMonth())
        else -> withMonth(3).with(TemporalAdjusters.lastDayOfMonth())
    }
}

fun Date.asLocalDateTime() : LocalDateTime {
    return Instant.ofEpochMilli(time).atZone(systemZoneId).toLocalDateTime()
}

fun Date.asLocalDate() : LocalDate {
    return Instant.ofEpochMilli(time).atZone(systemZoneId).toLocalDate()
}